/**
* 
* Kernel Simulator 
* Copyright (c) 2009 Ecole Centrale de Nantes IRCCyN  
* 
* see <a href="{@docRoot}/Documentation/kernelmessage.html"> for more information</a>
* 
* @author: Urunuela Richard
* @version 3.2
*/
package storm;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Vector;

import log.CpuTrace;
import log.messageTrace;
import log.setTrace;

import javax.swing.*;


import HIR.Dialog.ProgressDialog;
import HIR.relation.Table;



import programme.GUI.GUIProgramme;
import programme.GUI.InternalFrame;
import programme.GUI.JConsole;

import report.report;
import storm.Data.Data;
import storm.Processors.Processor;
import storm.Schedulers.Scheduler;
import storm.Schedulers.VirtualScheduler;
import storm.Tasks.Task;
import stormIO.SimulatorLoader;


/**
 * message order
 * @author richard
 *
 */
class ListeMessage extends ArrayList implements Comparator{
	public String name="Kernel";
	/**
	 * 
	 */
	public int compare(Object arg0, Object arg1) {
		KernelMessage M1 = (KernelMessage) arg0;
		KernelMessage M2 = (KernelMessage) arg1;
		Integer A = new Integer(( (int)M1.type.ordinal()));
		Integer B = new Integer(( (int)M2.type.ordinal()));
		return A.compareTo(B);
	}
}

/**
 * 
 * @author richard
 *
 */
	public class SimulationKernel extends Entity implements ISimulationKernel {

	/**
	 * Special entity
	 * 
	 */
	public setTrace Logs;
	private ITaskListManager tasksListeManager;
	public Scheduler scheduler;
	
	
	public double version = -1;
	/**
	 * time field
	 */
	int time; 
	public int duration;
	public double precision=1;
	
	/**
	 * List 
	 */
	public ArrayList  listeEntities;
	ArrayList  listeTimers;
	ListeMessage listeMessages;
	private int nombreTask;
	/**
	 * boolean control
	 */
	boolean kernelSelect = false; //indique si le kernel provoque le select du scheduler
	boolean useTimers = true;
	boolean gui=true;
	public boolean work=true;
	/**
	 * 	Color
	 */
	int R,G,B;
	public JConsole setOut=null;
	private report report;
	/**
	 * allTrace if true use external file to  create sonde and record all evt ....
	 */
	private boolean AllTrace;
	public SimulatorLoader xmlFile; 
	/* (non-Javadoc)
	 * @see storm.ISimulationKernel#setDuration(int)
	 */
	public void setDuration(int a){
		this.duration = a;
	}
	/* (non-Javadoc)
	 * @see storm.ISimulationKernel#getTime()
	 */
	public int getTime(){
		return this.time;
	}
	/* (non-Javadoc)
	 * @see storm.ISimulationKernel#addEntity(storm.Entity)
	 */
	public void addEntity(Entity A){
		A.Kernel = this;
		this.listeEntities.add(A);
		if(A instanceof TasksListeManager) 
		this.setTasksListeManager((ITaskListManager)A);
		if(A instanceof Task)
			this.getTasksListeManager().addTasks((Task)A);
		if(A instanceof Scheduler)
			this.scheduler = (Scheduler)A;
		if(A instanceof Processor){
			this.getTasksListeManager().addCPU((Processor)A);
		//	this.Logs.addTrace(new CpuTrace(-1,(Processor)A,null));
		}
		if(A instanceof Data)
			this.getTasksListeManager().addData((Data)A);
		int taille = this.listeEntities.size();
		double indice = (double)(taille);
		taille++;
		/**
		 * Color management
		 * TODO see best generation
		 */
		if(((A instanceof Task) || (A instanceof Processor))   && (A.color == null)){
			
			A.color = new Color(R,G,B);
			/*int p=G;
			//p = (int) (Math.random()* 255);
			G = p - 255 + 1;
			G=R;
			R=B;
			B=p;
			
			if (R> 10 ) {
				R = R-20;
				
			}
			if ((R<10)&&(B<10)&&(G<10)){
				G=255;
				R=B=0;
			}*/
			R =  (int) (Math.random()* 255);
			G =  (int) (Math.random()* 255);
			B = (int) (Math.random()* 255);
		}
		
		 	
	}
	/**
	 * Constructor :) 
	 */
	public SimulationKernel(){
		this.time=0;
		Logs = new setTrace();
		Logs.setKernel(this);
		listeEntities = new ArrayList();
		listeTimers = new ArrayList();
		listeMessages = new ListeMessage();
		listeEntities = new ArrayList();
		listeTimers = new ArrayList();
		TasksListeManager listeTask = new TasksListeManager();
		this.addEntity(listeTask);
		this.addEntity(this);	
		this.Kernel = this;
	
		//Logs.setDuration(this.duration);
		
		this.R=255;
		this.G=0;
		this.B=0;
	}
	public SimulationKernel(JConsole console) {
		
		this();
		this.setOut = console;
		
		// TODO Auto-generated constructor stub
	}
	/* (non-Javadoc)
	 * @see storm.ISimulationKernel#addTimer(storm.Timer)
	 */
	public void addTimer(Timer timer) {
		this.listeTimers.add(timer);
	}
	/**
	 * 
	 * Solve timer
	 * 
	 * see <a href="{@docRoot}/Documentation/timer.html"> for more information</a>
	 */
	private void solveTimer(){
		int i;
		Timer crt;
		ArrayList toInvoke = new ArrayList();
	    for(i=0 ;i < this.listeTimers.size();){
			crt = (Timer) this.listeTimers.get(i);
			debug_mess ("----> [TIMER SOLVE :" + i +  " ]   "+ crt.date + "  "+ crt.prop,10);
			if (crt.date > 1) {
				crt.date = crt.date - 1;
				i++;
			}
			else{
				toInvoke.add(crt);
				this.listeTimers.remove(crt);
			}
		}
		for (int j=0; j < toInvoke.size();j++){
			crt = (Timer) toInvoke.get(j);
			try{
				 debug_mess ("[TIMER INVOKE ] "+ crt.prop,1);
				 crt.fonction.invoke(crt.prop, (java.lang.Object[])null);
			}
		 catch (Exception e) {
			 debug_mess ("[HArg Timer ERROR] no invocation",1);
			 JOptionPane.showMessageDialog(null,
					    "Internal error target invocation" + crt.fonction.toString() + " is not know with timer",
					    "Inane error",
					    JOptionPane.ERROR_MESSAGE);
			e.printStackTrace();
		 	}
			
		}
			
	}
	/* (non-Javadoc)
	 * @see storm.ISimulationKernel#start(programme.GUI.GUIProgramme)
	 */
	public void start(GUIProgramme myInterface){
		debug_mess("[Run Kernel ] ",10);
		Entity crt;
		int i;
		/**
		 * Init Log
		 * 
		 */
		Logs.init(this);
		/**
		 * Pre boucle
		 */
		for(i = 0; i< this.listeEntities.size();i++){
			debug_mess ("	Boucle  Initialisation:  ]   ",10);
			crt = (Entity) this.listeEntities.get(i);
			/**
			 * call entity.init()
			 */
			crt.init();
			/**
			 * log pour INFO Task 
			 */
			if(crt instanceof Task){
				Task T= (Task) crt;
				this.debug_mess("[TASK ] "+T +" WCET "+ T.WCET+ " PERIOD "+ T.period+ " PRIO " + T.priority + " Activation date " + T.activationDate,1);
				nombreTask++;
				}
			if(crt instanceof Data){
				Data D = (Data) crt;
				this.debug_mess("[DATA ] "+D +" D "+ D.getDestination()+ " Source "+ D.getSource()+ " SIZE " + D.getSize(),1);
			}
		}

		/**
		 * Progress bar
		 */
		JProgressBar progressBar = null; 
		InternalFrame Myframe = null;
		if (myInterface != null ){
		        progressBar = new JProgressBar(0, this.duration);
		        progressBar.setValue(0);
		        progressBar.setStringPainted(true);
		        Myframe = new InternalFrame ("Load");
		        Myframe.getContentPane().add(progressBar);
			    Myframe.setBounds(10, 10, 300, 75);
			    Myframe.setResizeable(false);
			    myInterface.getLayeredPane().add(Myframe);
		}
		/**
		 * Blocking 
		 * To do : maybe not block console .... ?
		 */
		
		while(true){
			/**
			 * Test if virtual schedulability is possible
			 */
		
			
			if (myInterface != null ){
				if (time % 100 == 0){
				progressBar.setValue(this.time);
				Graphics2D screenGraphics =(Graphics2D) myInterface.getGraphics(); 
				myInterface.paint(screenGraphics);
				}
			}
			/**
			 * begin work 
			 */			
			debug_mess("--------------------------------------------------------------------------------------------------------------------",10);
			debug_mess("[Boucle It??ration ] " + this.time + " ---- ",10);
			debug_mess("--------------------------------------------------------------------------------------------------------------------",10);
			/**
			 * message manager 
			 */
			KernelMessage mess=null;
			Iterator IEntities ;
			Entity courante;
			this.debug_mess("[Processing MESSAGES] " + this.listeMessages.size() , 10);
			
			
			this.solveMessage(); 
			
			/**
			 * Timer management
			 */
			
			if (useTimers) {
				this.solveTimer();
			}
			/**
			 * Specific call to scheduler
			 */
			
			this.scheduler.sched();
			
			/**
			 * Log system 
			 */
			Iterator ll = this.getTasksListeManager().getProcessors().iterator();
			while(ll.hasNext()){
				Processor p= (Processor) ll.next();
				this.debug_mess("[LOG Scheduler task running] "+ p.getrunning()+ " On Processor"  + p, 1);
				if (p.isRunning()) {
					this.Logs.addTrace(new CpuTrace(this.time,p,p.getrunning()));
				}
			}
			
			/**
			 * Time mangemement (tick event) sched ever done 
			 * Order sched > Task > Data
			 */
			for(i = 0; i< this.listeEntities.size();i++){
				debug_mess ("	Boucle  Initialisation:  ]   ",10);
				crt = (Entity) this.listeEntities.get(i);
			//	if (crt instanceof storm.DPMS.DPM ) {
					crt.onTick();
				//}
			}
			/**
			 * Data mangement (produce/consume) 
			 */
			this.getTasksListeManager().processingConstraint();
			/**
			 * increase time 
			 */
			this.setTime(this.time + 1 );
			//time++;
			/**
			 * Stop condition
			 */
			if (time>duration) {
		
				this.work=false;
				if (myInterface != null ){
					myInterface.getLayeredPane().remove(Myframe);
					Graphics2D screenGraphics =(Graphics2D) myInterface.getGraphics(); 
					myInterface.paint(screenGraphics);
				}
				break;
			}
		}
	}

	private void setTime(int i) {
		// TODO Auto-generated method stub
		this.time = i ;
	}
	/* (non-Javadoc)
	 * @see storm.ISimulationKernel#solveMessage()
	 */
	public void solveMessage() {
		/**
		 * message have an order ....
		 */
		Collections.sort(listeMessages,listeMessages);
		while (this.listeMessages.size()> 0){
					KernelMessage mess = (KernelMessage) this.listeMessages.get(0);
			/**
			 * need too treat block before unblock
			 * for each message call entities choosen
			 */
			Iterator IEntities = this.listeEntities.iterator();
			while(IEntities.hasNext()){
				Entity courante=(Entity) IEntities.next();
				if (mess.D==courante) {
					this.debug_mess("[Traitement MESSAGE  "+  courante + "   ]  pour " + mess.D + " de " + mess.S + " Type " + mess.type + " Var1 "+ mess.param1  + " Var 2 " + mess.param2 ,10 );
					courante.processingMessage(mess);
				}
			}
			this.listeMessages.remove(0);
		}
	}
	/* (non-Javadoc)
	 * @see storm.ISimulationKernel#Launch(programme.GUI.GUIProgramme)
	 */
	public void Launch(GUIProgramme myInterface) {
		debug_mess("[Launch Kernel ] ",10);
		this.start(myInterface);
		
	}
	
	/* (non-Javadoc)
	 * @see storm.ISimulationKernel#addMessage(storm.KernelMessage)
	 */
	public void addMessage(KernelMessage m) {
		this.Kernel.getLogs().addTrace(new messageTrace(this.Kernel.getTime(),m.S,m.D,m.type, messageTrace.Type.CREATE));
		
		 //message m = new message(s,d,mess);
		 this.listeMessages.add(m);
		// TODO Auto-generated method stub
		
	}
	/* (non-Javadoc)
	 * @see storm.ISimulationKernel#getDuration()
	 */
	public int getDuration() {
		return duration;
	}
	/* (non-Javadoc)
	 * @see storm.ISimulationKernel#getName()
	 */
	public String getName(){
		return this.toString();
	}
	/* (non-Javadoc)
	 * @see storm.ISimulationKernel#getCtx()
	 */
	public String getCtx() {
		Iterator i=this.listeEntities.iterator();
		String res ="";
		while(i.hasNext()){
			Entity E = (Entity) i.next();
			if ((E instanceof Task) || (E instanceof Processor)) {
				res = res + "ID: " +E.getId() + " -- " + E.name;
				res = res + "\n";
			}
		}
		// TODO Auto-generated method stub
		return res;
	}
	/* (non-Javadoc)
	 * @see storm.ISimulationKernel#setTasksListeManager(storm.TasksListeManager)
	 */
	public void setTasksListeManager(ITaskListManager tasksListeManager) {
		this.tasksListeManager = tasksListeManager;
	}
	/* (non-Javadoc)
	 * @see storm.ISimulationKernel#getTasksListeManager()
	 */
	public ITaskListManager getTasksListeManager() {
		return tasksListeManager;
	}
	/* (non-Javadoc)
	 * @see storm.ISimulationKernel#getScheduler()
	 */
	public Scheduler getScheduler() {
		// TODO Auto-generated method stub
		return this.scheduler;
	}
	/* (non-Javadoc)
	 * @see storm.ISimulationKernel#getData()
	 */
	public ArrayList getData() {
		// TODO Auto-generated method stub
		return this.tasksListeManager.getDatas();
	}
	/* (non-Javadoc)
	 * @see storm.ISimulationKernel#setPrecision(double)
	 */
	public void setPrecision(double stepTime2) {
		this.precision = stepTime2;
		// TODO Auto-generated method stub
		
	}
	/* (non-Javadoc)
	 * @see storm.ISimulationKernel#setReport(report.report)
	 */
	public void setReport(report report) {
		this.report = report;
		// TODO Auto-generated method stub
	}
	/* (non-Javadoc)
	 * @see storm.ISimulationKernel#getReport()
	 */
	public report getReport() {
		return this.report;
		// TODO Auto-generated method stub
	}
	/**
	 * 
	 * @param j
	 * @return
	 */
	public boolean isCPU(int j) {
		Iterator i=this.listeEntities.iterator();
		String res ="";
		while(i.hasNext()){
			Entity E = (Entity) i.next();
			if (E.getId() == j ) {
				if (E instanceof Processor ) {
					return true;
				}
				else return false;
			}
		}
		// TODO Auto-generated method stub
		return false;
	}
	/**
	 * 
	 * @param j
	 * @return
	 */
	public boolean isTask(int j) {
		Iterator i=this.listeEntities.iterator();
		String res ="";
		while(i.hasNext()){
			Entity E = (Entity) i.next();
			if (E.getId() == j ) {
				if (E instanceof Task ) {
					return true;
				}
				else return false;
			}
		}
		// TODO Auto-generated method stub
		return false;
	}
	/* (non-Javadoc)
	 * @see storm.ISimulationKernel#stop()
	 */
	public void stop() {
		this.time=this.duration;
		// TODO Auto-generated method stub
	}
	/* (non-Javadoc)
	 * @see storm.ISimulationKernel#getAllTrace()
	 */
	public boolean getAllTrace() {
		// TODO Auto-generated method stub
		return this.AllTrace;
	}
	/* (non-Javadoc)
	 * @see storm.ISimulationKernel#setAllTrace(boolean)
	 */
	public void setAllTrace(boolean allTrace2) {
	
		this.AllTrace= allTrace2;
		// TODO Auto-generated method stub
		
	}
	/* (non-Javadoc)
	 * @see storm.ISimulationKernel#showerror(java.lang.String)
	 */
	public void showerror(String string) {
		if (this.setOut != null) {
			this.setOut.afficheError(string);
		}
		// TODO Auto-generated method stub
		
	}
	/* (non-Javadoc)
	 * @see storm.ISimulationKernel#freeAll()
	 */
	public void freeAll() {
		this.listeEntities = new ArrayList();
		this.Logs.freeAll();
		/*Iterator i = this.listeEntities.iterator();
		while(i.hasNext()){
			//Entity E = (Entity) i.next();
			E.freeAll();
		}
		this.listeEntities = new ArrayList();
		// TODO Auto-generated method stub
		*/
	}

	
	/* (non-Javadoc)
	 * @see storm.ISimulationKernel#startSimu(stormIO.SimulatorLoader, java.lang.String)
	 */

public String startSimu(stormIO.SimulatorLoader xmlFile, String simulationName) {
	Runtime.getRuntime().gc();
	this.xmlFile = xmlFile;
	String res = "";
	setDuration(xmlFile.duration);
	setPrecision(xmlFile.precision);
	setAllTrace(xmlFile.getAllTrace());
	addEntity(xmlFile.Sched);
	
	if (xmlFile.DPM != null){
		addEntity(xmlFile.DPM);
		
	}

	for (int j=0; j < xmlFile.CPUS.size(); j++){
		Processor P=(Processor) xmlFile.CPUS.get(j);
		addEntity(P);
	}
	for (int j=0; j < xmlFile.listTask.size(); j++){
		Task T=(Task) xmlFile.listTask.get(j);
		addEntity(T);
	}
	for (int j=0; j < xmlFile.listData.size(); j++){
		Data D=(Data) xmlFile.listData.get(j);
		
		addEntity(D);
	}
	
	if (xmlFile.Observer != null){
		addEntity(xmlFile.Observer);
		
	}	
	
	/*** Progrees bar
	 * 
	 */
	ProgressDialog progressBar = new ProgressDialog();

	
	progressBar.activate(this.duration);
	/*try {
		for (int i = 0 ; i < duration ; i++) {
			Thread.sleep(10);
			progressBar.setValue(i);
		}

		
	} catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}*/
	
	
	/***
	 * Debut simulation 
	 * 
	 * 
	 * 
	 */
	
	Entity crt;
	int i;
	
	/** 
	 * Pre boucle  d'init de toutes les entit?e
	 */
	/**
	 * Pre boucle
	 */
	for(i = 0; i< this.listeEntities.size();i++){
		
		crt = (Entity) this.listeEntities.get(i);
		crt.setKernel(this);
		/**
		 * call entity.init()
		 */
		crt.init();
	}
	
	if (this.scheduler.failed == true) {
	   
		progressBar.resume();
		return ("Failed to exec");
	}
	
		/**
		 * log pour INFO Task 
		 */
		while(true){
			progressBar.setValue(this.time);

			/**
			 * message manager 
			 */
			this.solveMessage();
			/**
			 * Timer management
			 */
			
			if (useTimers) {
				this.solveTimer();
			}
			/**
			 * Specific call to scheduler
			 */
			
			this.scheduler.sched();
			/**
			 * Log system 
			 */
			Iterator ll = this.getTasksListeManager().getProcessors().iterator();
			while(ll.hasNext()){
				Processor p= (Processor) ll.next();
				if (p.isRunning()) {
					this.Logs.addTrace(new CpuTrace(this.time,p,p.getrunning()));
				}
			}
			/**
			 * Time mangemement (tick event) sched ever done 
			 * Order sched > Task > Data
			 */
			for(i = 0; i< this.listeEntities.size();i++){
				crt = (Entity) this.listeEntities.get(i);
				if (!(crt instanceof storm.DPMS.DPM ) ){
					crt.onTick();
				}
			}
			for(i = 0; i< this.listeEntities.size();i++){
				crt = (Entity) this.listeEntities.get(i);
				if ((crt instanceof storm.DPMS.DPM ) ){
					crt.onTick();
				}
			}
			/**
			 * 
			 * Log stat in file 
			 */
			for(i = 0; i< this.listeEntities.size();i++){
				crt = (Entity) this.listeEntities.get(i);
				if ((getAllTrace()) && (crt.mySonde != null )) {
				
					crt.mySonde.proceed();
				
				}
			}
			/**
			 * Data mangement (produce/consume) 
			 */
			this.getTasksListeManager().processingConstraint();
			/**
			 * increase time 
			 */
			this.setTime(this.time + 1 );
			//time++;
			/**
			 * Stop condition
			 */
			if (time>duration) {
		
				this.work=false;
				break;
			}

			
			
		}

		
		
		
		
	
	
	
	
	
	
	progressBar.resume();
	return res;
}
	/* (non-Javadoc)
	 * @see storm.ISimulationKernel#getTaskTable()
	 */
	/*public ArrayList getTaskTable() {
		ArrayList L= this.tasksListeManager.getALLTask();
		return L;
	}*/
	public setTrace getLogs() {
		// TODO Auto-generated method stub
		return this.Logs;
	}
	public double getprecision() {
		// TODO Auto-generated method stub
		return this.precision;
	}
	public  ArrayList getlisteEntities(){
		return  this.listeEntities;
	}
	public ArrayList getProcessorTable() {
		// TODO Auto-generated method stub
		ArrayList L= this.tasksListeManager.getALLProcessors();
		return L;
	}
	public Entity getEntitie(int source) {
		// TODO Auto-generated method stub
		Iterator I = this.listeEntities.iterator();
		Entity E = null;
		while (I.hasNext()){
			
			E = (Entity) I.next();
			if (E.getId() == source) return E;
		}
		
		E = this.scheduler;
		if (E.getId() == source ) return E;
		if (this.scheduler instanceof VirtualScheduler) {
			I = ((VirtualScheduler)this.scheduler).getAllSchedulers().iterator();
			while (I.hasNext()){
				E = (Entity) I.next();
				if (E.getId() == source) return E;
				
			}
		}
		return E;
	}

/** Progress bar
 * 
 * 
 * @author richard
 *
 */

}
